package org.jackysoft.codetool;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jackysoft.annotations.Form;
import org.jackysoft.annotations.FormElement;
import org.jackysoft.codetool.templates.TemplatePosition;
import org.jackysoft.util.IOUtils;
import org.jackysoft.util.StringUtils;
import org.springframework.stereotype.Component;

import com.google.common.collect.Sets;

import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModelIterator;

@Component
public final class CodeGenerator {
	private static Log logger = LogFactory.getLog(CodeGenerator.class);
	private static Set<String> views = Sets.newHashSet();
	private Set<String> jsFiles = Sets.newHashSet();
	private Set<String> cssFiles = Sets.newHashSet();
	private Configuration cfg = new Configuration();
	{
		views.add("add.jsp.ftl");
		views.add("list.jsp.ftl");
		views.add("remove.jsp.ftl");
		views.add("update.jsp.ftl");
		views.add("input.jsp.ftl");
		views.add("get.jsp.ftl");
		views.add("gets.jsp.ftl");

		jsFiles.add("add.js.ftl");
		jsFiles.add("list.js.ftl");
		jsFiles.add("remove.js.ftl");
		jsFiles.add("update.js.ftl");
		jsFiles.add("input.js.ftl");
		jsFiles.add("get.js.ftl");

		cssFiles.add("add.css.ftl");
		cssFiles.add("list.css.ftl");
		cssFiles.add("remove.css.ftl");
		cssFiles.add("update.css.ftl");
		cssFiles.add("input.css.ftl");
		cssFiles.add("get.css.ftl");

		ClassTemplateLoader cloader = new ClassTemplateLoader(TemplatePosition.class, "");
		cfg.setTemplateLoader(cloader);

		cfg.setObjectWrapper(DefaultObjectWrapper.getDefaultInstance());
		cfg.setDefaultEncoding("UTF-8");

	}

	/**
	 * 生成三层代码
	 * 
	 * @author HANTU
	 * @param projectName 目标eclipse工程名称
	 * @param entityName  实体名称
	 * @param nosqlable   是否nosql数据库 
	 * @param workspace   eclipse工作空间绝对路径路径
	 * <pre>
	 *  example use:
	 *  genCode("netfile","HistoryFileItem",false,"f:/java-workspace");
	 * 
	 * </pre>
	 */
	public void generateJavaCode(String projectName, String entityName,
			boolean nosqlable, String workspace,boolean entityExists) throws IOException,
			TemplateException {
		String groupId = "jackysoft";
		String actionSpace = projectName
				.substring(projectName.lastIndexOf("-") + 1);
		String srcDirectory = "src" + File.separator + "main" + File.separator
				+ "java" + File.separator;
		String entityPath = workspace + File.separator + projectName
				+ File.separator + srcDirectory + "org" + File.separator
				+ groupId + File.separator + "entity" + File.separator
				+ entityName + ".java";
		String actionPath = workspace + File.separator + projectName
				+ File.separator + srcDirectory + "org" + File.separator
				+ groupId + File.separator + "struts2" + File.separator
				+ actionSpace + File.separator + entityName + "Action.java";
		String servicePath = workspace + File.separator + projectName
				+ File.separator + srcDirectory + "org" + File.separator
				+ groupId + File.separator + "service" + File.separator
				+ entityName + "Service.java";
		String serviceImplPath = workspace + File.separator + projectName
				+ File.separator + srcDirectory + "org" + File.separator
				+ groupId + File.separator + "service" + File.separator
				+ "impl" + File.separator + entityName + "ServiceImpl.java";	
		IOUtils.makeDirectories(
				new File(entityPath).getParentFile(),
				new File(actionPath).getParentFile(),
				new File(servicePath).getParentFile(),
				new File(serviceImplPath).getParentFile());
		
		Template temp = null;

		/* Create a data-model */
		SimpleHash root = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		root.put("nameSpace", "org."+groupId);
		root.put("actionSpace", actionSpace);
		
		root.put("entityName", entityName);
		root.put("nosqlable", nosqlable);
		SimpleHash metas = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		if(!entityExists)
		metas.put("Entity.ftl", entityPath);
		metas.put("Action.ftl", actionPath);
		metas.put("Service.ftl", servicePath);		
		metas.put("ServiceImpl.ftl", serviceImplPath);

		Writer fw = null;

		TemplateModelIterator itr = metas.keys().iterator();
		while (itr.hasNext()) {
			String tname = itr.next().toString();
			temp = cfg.getTemplate(tname);
			temp.setEncoding("UTF-8");
			String fname = metas.get(tname).toString();
			File fi = new File(fname);
			fw = new FileWriter(fi);
			temp.process(root, fw);
			fw.close();
			logger.info("文件 " + fname + "  已生成  ");
		}

	}

	/**
	 * <ul>
	 * <li>start 显示添加实体的界面
	 * <li>list 显示列表页面的界面
	 * <li>get 显示编辑页面的界面
	 * </ul>
	 * @param cls 实体类元数据
	 * @param workspace eclipse工作空间
	 * @param viewProjectName 视图层工程名
	 * @param webapp web工程目录
	 * @param viewdir 表示层相对路径
	 * @param jsPath js文件相对路径
	 * @param cssPath css文件相对路径
	 * */

	public void generateViews(Class<?> cls, String workspace,
			String viewProjectName,String webapp,String viewdir, String jsPath,
			String cssPath) throws IOException, TemplateException {
		SimpleHash root = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		String entityName = cls.getSimpleName();
		root.put("entityName", entityName);
		if (cls.isAnnotationPresent(Form.class)) {
			Form form = cls.getAnnotation(Form.class);
			SimpleHash formobj = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
			formobj.put("id", form.id());
			formobj.put("action", form.action());
			formobj.put("label", form.label());
			formobj.put("enctype", form.enctype().toString());
			formobj.put("method", form.method().toString());
			formobj.put("className", form.className());
			formobj.put("target", form.target());
			root.put("form", formobj);
		}
		Method[] gets = cls.getMethods();
		SimpleHash fields = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		for (Method getm : gets) {
			if (getm.isAnnotationPresent(FormElement.class)) {
				String name = getm.getName();
				String indexName = StringUtils
						.lowerFirstChar(name.substring(3));
				name = "bean." + indexName;
				FormElement fe = getm.getAnnotation(FormElement.class);
				SimpleHash fs = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
				fs.put("id", fe.id());
				fs.put("name", name);
				fs.put("indexName", indexName);
				fs.put("type", fe.type().toString());
				fs.put("label", fe.label());
				fs.put("listable", fe.listable() + "");
				fs.put("value", fe.value());
				fields.put(indexName, fs);

			}
		}
		String webdir = webapp;
		String viewPath = webdir +File.separator+ viewdir;
		root.put("formfields", fields);
		String viewBase = workspace + File.separator + viewProjectName
				+ File.separator + viewPath + File.separator
				+ entityName.toLowerCase();
		String jsBase = workspace + File.separator + viewProjectName
				+ File.separator + webdir + File.separator + jsPath
				+ File.separator + "apps" + File.separator
				+ entityName.toLowerCase();

		String cssBase = workspace + File.separator + viewProjectName
				+ File.separator + webdir + File.separator + cssPath
				+ File.separator + "apps" + File.separator
				+ entityName.toLowerCase();

		logger.info("生成表示层...");
		
		
		
		
		// 生成各种action页面			
		writeFiles(views,viewBase,root,cfg,entityName);		
		// 生成js
		writeFiles(jsFiles,jsBase,root,cfg,null);
		// 生成css
		writeFiles(cssFiles,cssBase,root,cfg,null);
		logger.info("生成表示层... [OK]");

	}
	void writeSuccess(String actionName,String template,String base,SimpleHash root,Configuration cfg) throws IOException, TemplateException{
		File vpath = new File(base);
		if (!vpath.exists()) {
			vpath.mkdir();
		}		
		String name = actionName+".jsp";
		File vf = new File(vpath, name);
		Template vtemp = cfg.getTemplate(template);
		vtemp.setEncoding("UTF-8");
		vtemp.process(root, new FileWriter(vf));
		logger.info("文件 :" + vf.getAbsolutePath()+" 已经生成!");
		
	}
	void writeFiles(Set<String> templates,String base,SimpleHash root,Configuration cfg,String entityName) throws IOException, TemplateException{
		File vpath = new File(base);
		IOUtils.makeDirectory(vpath);
		String prefix  = "";
		if(entityName!=null){
			String actionName = parseActionName(entityName);
			writeSuccess(actionName,"success.jsp.ftl",base,root,cfg);
			prefix = (actionName!=null)?actionName+"-":"";
		}
		for (String view : templates) {
			int linx = view.lastIndexOf('.');
			String name = prefix+view.substring(0, linx);
			File vf = new File(vpath, name);
			Template vtemp = cfg.getTemplate(view);
			vtemp.setEncoding("UTF-8");
			vtemp.process(root, new FileWriter(vf));
			logger.info("文件 :" + vf.getAbsolutePath()+" 已经生成!");
		}
	}
	
	String parseActionName(String entityName){
		if(entityName==null)
			throw new NullPointerException("实体名不能为null");
		char[] chars  = entityName.toCharArray();
		String name = (chars[0]+"").toLowerCase();
		for(int i=1;i<chars.length;i++){
			char c  =chars[i];
			if((c+"").equals((c+"").toUpperCase())){
				name=name+"-"+(c+"").toLowerCase();
			}else{
				name+=c;
			}
			
		}
		
		
		return name;
		
	}
	
	

}
